/**
 * 继承，继承中的构造方法
 * 抽象类
 * 接口
 * Mixins,操作符的复写
 */
main(List<String> args) {
  var student = Student();
  student.study();

  student.name = 'Tom';
  student.age = 15;
  student._birthday = '2020';

  print(student.isAdult);

  student.run();

  // 多态
  Person person = Student();
  // person.studey();
  if (person is Student) {
    person.study();
  }

  print(person);

  // 默认调用父类无名无参构造方法
  // var s = S1();
  var s2 = S1('Tom');
  print(s2.name);

  var s3 = S2('Bob', 'Male');
  print(s3);

  // var p1 = new P3();
  var ss = S3();
  ss.run();

  var d = D();
  d.a();
  d.b();
  d.c();

  var pp1 = P6(20);
  var pp2 = P6(20);

  print(pp1 > pp2);

  print(pp1['age']);

  print(pp1 == pp2);
}

/**
 * 继承
 * 
 * 使用 extends 关键字继承一个类
 * 子类会继承父类可见的属性和方法，不会继承构造方法
 * 子类能够复写父类的方法，getter和setter
 * 单继承，多态性
 */
class Person {
  String name;
  int age;
  String _birthday;

  bool get isAdult => age > 18;

  run() {
    print('Person run ...$_birthday');
  }
}

class Student extends Person {
  study() {
    print('Student Study ...');
  }

  // @override可省略
  @override
  bool get isAdult => age > 14;

  @override
  run() {
    print('Student Run ...');
    // super.run();
  }

  // 复写 object 基类中的方法
  @override
  String toString() {
    return 'Name is $name, age = $age';
  }
}

/**
 * 子类的构造方法默认会调用父类的无名无参的构造方法
 * 如果父类没有无名无参构造方法，则需要显示调用父类构造方法
 * 在构造方法后使用 : 显示调用父类构造方法
 */
class P1 {
  String name;

  // 无名无参
  P1() {
    print('Person P1...');
  }

  // 有名有参
  P1.withName(this.name);
}

class S1 extends P1 {
  int age;

  S1(String name) : super.withName(name);
}

/**
 * 父类构造方法在子类构造方法体开始执行的位置调用
 * 如果有初始化列表，初始化列表会在父类构造方法之前执行
 */
class P2 {
  String name;

  // 无名无参
  P2() {
    print('Person P1...');
  }

  // 有名有参
  P2.withName(this.name);
}

class S2 extends P2 {
  int age;

  final String gender;

  S2(String name, String gender)
      : gender = gender,
        super.withName(name);
}

/**
 * 抽象类
 * 
 * 抽象类使用 abstract 表示，不能直接被实例化
 * 抽象方法不用 abstract 修饰，无实现
 * 抽象类可以没有抽象方法
 * 有抽象方法的类一定得声明为抽象类
 */
abstract class P3 {
  // 抽象方法
  run();

  run1() {}
}

class S3 extends P3 {
  @override
  run() {
    print('run...');
  }
}

/**
 * 接口
 * 
 * 类和接口是统一的，类就是接口
 * 每个类都隐式的定义了一个包含所有实例成员的接口
 * 如果是复用已有类的实现，使用继承extends
 * 如果只是使用已有类的外在行为，使用接口implements
 */
class P4 {
  String name;

  int get age => 18;

  run() {
    print('P4 run...');
  }
}

class S4 implements P4 {
  @override
  String name;

  @override
  int get age => 15;

  @override
  run() {
    print('S4 run...');
  }
}

// 使用抽象类实现接口
abstract class P5 {
  run();
}

class S5 implements P5 {
  @override
  run() {
    print('S5');
  }
}

/**
 * Mixins
 * 
 * Mixins 类似于多继承，是在多继承中重用一个类代码的方式
 * 作为Mixins的类，不能有显示声明构造方法
 * 作为Mixins的类，只能继承自Object（不能有继承，但是可以有接口实现）
 * 使用关键字 with 连接一个或多个Mixins
 */
class A {
  a() {
    print("A.a()...");
  }
}

class B {
  a() {
    print('B.a()...');
  }

  b() {
    print('B.b()...');
  }
}

class Test {}

class C {
// class C extends Test {
  // C() {}

  a() {
    print('C.a()...');
  }

  c() {
    print('C.c()...');
  }
}

// 必须有继承，才能使用with
// 重名方法的调用，是按顺序调用Mixins的最后一个的实现
class D extends A with B, C {}

// 简写的方式
class E = C with B;
// class E extends C with B {}

/**
 * 操作符复写
 * 
 * 复写操作符，需要在类中定义 operator 关键字
 * 如果需要复写==，还需要复写对象的hashCode getter方法
 * 可覆写的操作符如下：
 * < > <= >= - + / ~/ * % | ^ & << >> [] []= ~ ==
 */
class P6 {
  int age;

  P6(this.age);

  bool operator >(P6 p) {
    return this.age > p.age;
  }

  int operator [](String field) {
    if (field == 'age') {
      return age;
    }
    return 0;
  }

  bool operator ==(Object other) =>
      identical(this, other) ||
      other is P6 && runtimeType == other.runtimeType && age == other.age;

  @override
  int get hashCode => age.hashCode;
}
